﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using TrainingCenterSystem.UserManagement;
using TrainingCenterSystem.VocabularyManagement.Exceptions;

namespace TrainingCenterSystem.VocabularyManagement
{
    /// <summary>
    /// A helper class for reading collection files
    /// </summary>
    public class CollectionFileReader
    {
        #region private members
        private XmlTextReader _xmlReader;

        private VocabularyCollection _collection=null;

        private TrainingCenter _trainingCenter;

        private string _internalReadingError;

        private string _accessRightError;
             
        #endregion

        #region setters and getters
        /// <summary>
        /// Gets the collection that was read from a file. 
        /// </summary>
        public VocabularyCollection Collection
        {
            get { return _collection; }
        }
        #endregion

        #region constructor(s)
        /// <summary>
        /// Creates an instance of the file reader.
        /// </summary>
        /// <param name="filename">the filename</param>
        /// <param name="trainingCenter">the instance of the TrainingCenter</param>
        public CollectionFileReader(TrainingCenter trainingCenter, string internalError, string accessError)
        {
            _trainingCenter = trainingCenter;
            _internalReadingError = internalError;
            _accessRightError = accessError;
            //this.read(filename);
            
        }
        #endregion

        #region methods
        /// <summary>
        /// validates the access rights.
        /// </summary>
        /// <param name="currentUser">the current user trying to open a file.</param>
        /// <returns>true, if the access rights are not violated.</returns>
        private bool validateAccessRights(User currentUser)
        {
            if (_collection.CollectionAccess == CollectionAccess.Private)
            {
                if (currentUser.Username != _collection.CollectionUser.Username)
                    return false;
            }
            
            return true;
        }

        /// <summary>
        /// Reads the xml-file.
        /// </summary>
        /// <param name="filename">the filename</param>
        public void Read(string filename)
        {           
            _xmlReader = new XmlTextReader(filename);
                        
            while (_xmlReader.Read())
            {
                if ((_xmlReader.NodeType == XmlNodeType.Element)&& (_xmlReader.Name == "VocabularyCollection"))
                {
                    this.readCollectionAttributes();
                }
                else if ((_xmlReader.NodeType == XmlNodeType.Element)&&(_xmlReader.Name == "Context"))
                {
                    this.readContextData();
                    _collection.FileName = filename;
                }

                else if ((_xmlReader.NodeType == XmlNodeType.Element) && (_xmlReader.Name == "Vocabularies"))
                {
                    this.readVocabularies();                   
                }               
            }

            if (!this.validateAccessRights(_trainingCenter.CurrentUser))
                throw new CollectionFileReadingException(_accessRightError);

            _collection.FileName = filename;
            _xmlReader.Close();
        }

        /// <summary>
        /// private helper method to read the attrivbutes of a collection.
        /// </summary>
        private void readCollectionAttributes()
        {
            string name=null;
            string description=null;
            string username=null; 
            string accesstype=null;
            
            XmlReader subReader = _xmlReader.ReadSubtree();
            while (subReader.Read())
            {
                if (subReader.Name == "CollectionName")
                {
                    name = subReader.ReadString();
                }
                else if (subReader.Name == "Description")
                {
                    description = subReader.ReadString();
                }
                else if (subReader.Name == "AccessType")
                {
                    accesstype = subReader.ReadString();
                }
                else if (subReader.Name == "Username")
                {
                    username = subReader.ReadString();
                }
                else if (subReader.Name == "Contexts")
                {                    
                    break;
                }
            }

            if ((username == null) || (accesstype == null) || (name == null))
            {
                throw new CollectionFileReadingException(_internalReadingError);                
            }
            else
            {
                // create accesstype
                CollectionAccess access;                 
                if (accesstype == "ReadOnly")
                    access = CollectionAccess.ReadOnly;
                else if (accesstype == "ReadWrite")
                    access = CollectionAccess.ReadWrite;
                else
                    access = CollectionAccess.Private;

                // create the user                
                User user = UserHandler.GetUser(_trainingCenter.AvailableUsers, new User(null, username));
                
                //if the user exists, then create the collection
                if (user!=null)
                    _collection = new VocabularyCollection(name, user, access, description);
                else
                    throw new CollectionFileReadingException(_internalReadingError);
            }      
        }

        public void CloseReader()
        {
            _xmlReader.Close();
        }

        /// <summary>
        /// Reads the given context data.
        /// </summary>
        private void readContextData()
        {            
            do
            {
                string id = _xmlReader.GetAttribute("id");
                string name = _xmlReader.ReadString();
                VocabularyContext context = new VocabularyContext(id, name, null);
                _collection.addContext(context);
            } while (_xmlReader.ReadToNextSibling("Context"));
        }

        /// <summary>
        /// Reads the existing vocabularies
        /// </summary>
        private void readVocabularies()
        {
            while (_xmlReader.Read())
            {
                if (_xmlReader.NodeType == XmlNodeType.Whitespace)
                    continue;
                if (_xmlReader.Name == "GenericWord")
                {
                    this.readGeneric();
                }
                else if (_xmlReader.Name == "Noun")
                {
                    this.readNoun();
                }
                else if (_xmlReader.Name == "Verb")
                {
                    this.readVerb();
                }
                else if (_xmlReader.Name == "Adjective")
                {
                    this.readAdjective();
                }
            }
        }

        /// <summary>
        /// reads the common attributes for each vocabulary.
        /// </summary>
        /// <param name="subReader">an instance of the XMLReader class</param>
        /// <returns>a generic word</returns>
        private GenericWord readCommonAttributes(XmlReader subReader)
        {
            GenericWord aWord = new GenericWord();
            //subReader = _xmlReader.ReadSubtree();
            while (subReader.Read())
            {
                if (subReader.NodeType == XmlNodeType.Whitespace)
                    continue;
                if (subReader.Name == "BaseValue")
                {
                    aWord.BaseValue = subReader.ReadString();
                }
                else if (subReader.Name == "Translations")
                {
                    aWord.Translations = subReader.ReadString();
                }
                else if (subReader.Name == "ContextName")
                {
                    aWord.ContextName = subReader.ReadString();
                    aWord.SetContext(this.lookupContext(aWord));
                }
                else if (subReader.Name == "CollectionName")
                {
                    aWord.CollectionName = subReader.ReadString();
                }
            }
            return aWord;
        }

        /// <summary>
        /// Tracks down the context of a given word.
        /// </summary>
        /// <param name="aWord">a generic word</param>
        private VocabularyContext lookupContext(GenericWord aWord)
        {
            for (int i = 0; i < _collection.Contexts.Count; i++)
            {
                if (aWord.ContextName == ((VocabularyContext)_collection.Contexts[i]).ContextName)
                    return (VocabularyContext)_collection.Contexts[i];               
            }
            return null;
        }       
                
        /// <summary>
        /// Reads a generic word
        /// </summary>
        private void readGeneric()
        {
            XmlReader subReader = _xmlReader.ReadSubtree();            
            GenericWord aWord=this.readCommonAttributes(subReader);

            _collection.AddWord(aWord);
        }

        /// <summary>
        /// Reads a noun
        /// </summary>
        private void readNoun()
        {
            XmlReader subReader = _xmlReader.ReadSubtree();
            Noun aNoun = new Noun();
            // (Noun)this.readCommonAttributes(subReader);
            
            while (subReader.Read())
            {
                if (subReader.Name == "Article")
                {
                    aNoun.Article = subReader.ReadString();
                }
                else if (subReader.Name == "DefinedSing")
                {
                    aNoun.DefinedSingular = subReader.ReadString();
                }
                else if (subReader.Name == "UndefinedSing")
                {
                    aNoun.UndefinedSingular = subReader.ReadString();
                }
                else if (subReader.Name == "DefinedPl")
                {
                    aNoun.DefinedPlural = subReader.ReadString();
                }
                else if (subReader.Name == "UndefinedPl")
                {
                    aNoun.UndefinedPlural = subReader.ReadString();
                }
                else if (subReader.Name=="BaseValue")
                {
                    aNoun.BaseValue=subReader.ReadString();
                }
                else if (subReader.Name=="Translations")
                {
                    aNoun.Translations=subReader.ReadString();
                }
                else if (subReader.Name=="CollectionName")
                {
                    aNoun.CollectionName=subReader.ReadString();
                }
                else if (subReader.Name=="ContextName")
                {
                    aNoun.ContextName=subReader.ReadString();
                    aNoun.SetContext(this.lookupContext(aNoun));
                }            
            }

            _collection.AddWord(aNoun);
        }

        /// <summary>
        /// Reads a verb
        /// </summary>
        private void readVerb()
        {
            XmlReader subReader = _xmlReader.ReadSubtree();
            //GenericWord aWord = this.readCommonAttributes(subReader);
            Verb aVerb = new Verb();            
                        
            string strIsRegular = null;
            
            while (subReader.Read())
            {
                if (subReader.NodeType == XmlNodeType.Whitespace)
                    continue;
                if (subReader.Name == "Present")
                {
                    aVerb.PresentForm = subReader.ReadString();
                }
                else if (subReader.Name == "Imperfect")
                {
                    aVerb.ImperfectForm = subReader.ReadString();
                }
                else if (subReader.Name == "Supinum")
                {
                    aVerb.Supinum = subReader.ReadString();
                }
                else if (subReader.Name == "ParticipPerfect")
                {
                    aVerb.ParticipPerfect = subReader.ReadString();
                }
                else if (subReader.Name == "IsRegular")
                {
                    strIsRegular = subReader.ReadString();
                    if (strIsRegular == "True")
                        aVerb.IsRegular = true;
                }
                else if (subReader.Name == "BaseValue")
                {
                    aVerb.BaseValue = subReader.ReadString();
                }
                else if (subReader.Name == "Translations")
                {
                    aVerb.Translations = subReader.ReadString();
                }
                else if (subReader.Name == "CollectionName")
                {
                    aVerb.CollectionName = subReader.ReadString();
                }
                else if (subReader.Name == "ContextName")
                {
                    aVerb.ContextName = subReader.ReadString();
                    aVerb.SetContext(this.lookupContext(aVerb));
                }
            }

            _collection.AddWord(aVerb);

        }

        /// <summary>
        /// Reads an adjective
        /// </summary>
        private void readAdjective()
        {
            XmlReader subReader = _xmlReader.ReadSubtree();
            //Adjective adj = (Adjective)this.readCommonAttributes(subReader);
            Adjective adj = new Adjective();          

            while (subReader.Read())
            {
                if (subReader.NodeType == XmlNodeType.Whitespace)
                    continue;
                if (subReader.Name == "Comparative")
                {
                    adj.Comparative = subReader.ReadString();
                }
                else if (subReader.Name == "Superlative")
                {
                    adj.Superlative = subReader.ReadString();
                }
                else if (subReader.Name == "BaseValue")
                {
                    adj.BaseValue = subReader.ReadString();
                }
                else if (subReader.Name == "Translations")
                {
                    adj.Translations = subReader.ReadString();
                }
                else if (subReader.Name == "CollectionName")
                {
                    adj.CollectionName = subReader.ReadString();
                }
                else if (subReader.Name == "ContextName")
                {
                    adj.ContextName = subReader.ReadString();
                    adj.SetContext(this.lookupContext(adj));
                }
            }

            _collection.AddWord(adj);
        }
        #endregion
    }
}
